#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <malloc.h>
#include <assert.h>
#include "collision.h"

int contactEnnemi (personnage* perso, personnage* Ennemi)
{
	assert (perso != NULL);
	if (fabs (perso->hitBox->pos.x - Ennemi->hitBox->pos.x) <= perso->hitBox->dim && fabs (perso->hitBox->pos.y - Ennemi->hitBox->pos.y) <= perso->hitBox->dim)
	{
		if (perso->hitBox->pos.x < Ennemi->hitBox->pos.x) /* Si le heros arrive sur l'ennemi par la gauche */
		{
			blocMouvement (perso->hitBox, -1*perso->hitBox->dim, 0);
		}
		else /* Si le heros arrive sur l'ennemi par la droite ou pile sur sa position */
		{
			blocMouvement (perso->hitBox, perso->hitBox->dim, 0);
		}
		return 1;
	}
	return 0;
}

int contactBloc (bloc* B1, bloc* B2)
{
	assert (B1 != NULL);
	assert (B2 != NULL);
	if (fabs (B1->pos.x + B1->dim/2 - B2->pos.x) <= (B1->dim + B2->dim)/2
        && fabs (B1->pos.y + B1->dim/2 - B2->pos.y) <= (B1->dim + B2->dim)/2)
	{
		return 1;
	}
	return 0;
}

int contactDeadZone (personnage* perso, bloc* DeadZone)
{
	assert (perso != NULL);
	assert (DeadZone != NULL);
    if (contactBloc(perso->hitBox, DeadZone) && DeadZone->solide == 3)
    {
        return 1;
    }
    return 0;
}

int contactMur (personnage* perso, bloc* Mur)
{
	assert (perso != NULL);
	assert (Mur != NULL);
	if (contactBloc(perso->hitBox, Mur) && Mur->solide == 2)
	{
		return 1;
	}
	return 0;
}

int contactSol (personnage* perso, bloc* Sol)
{
	assert (perso != NULL);
	assert (Sol != NULL);
	if (contactBloc(perso->hitBox, Sol) && Sol->solide == 1)
	{
		return 1;
	}
	return 0;
}

int projectileRencontreEnnemi (listeEnnemis* L, projectile* A)
{
	assert (L != NULL);
	assert (A != NULL);
	celluleEnnemi* Repere;
	int Place = 1;
	Repere = L->Boss;
	while (Repere != NULL)
	{
		if (contactBloc (Repere->Ennemi->hitBox, A->hitBox) == 0)
		{
			Repere = Repere->Suivant;
			Place++;
		}
		else
		{
			return Place;
		}
	}
	return 0;
}

int recherchePositionEnnemi (listeEnnemis* L, personnage* Ennemi)
{
	assert (L != NULL);
	assert (Ennemi != NULL);
	celluleEnnemi* Repere;
	int Place = 1;
	Repere = L->Boss;
	while (Repere != NULL)
	{
		if (Ennemi != Repere->Ennemi)
		{
			Repere = Repere->Suivant;
			Place++;
		}
		else
		{
			return Place;
		}
	}
	return 0;
}

int projectileSortDuTerrain (terrain* T, projectile* P)
{
	if (P->hitBox->pos.x >= T->largeur*T->tab[0][0]->dim || P->hitBox->pos.y >= T->hauteur*T->tab[0][0]->dim ||
        P->hitBox->pos.x < 0 || P->hitBox->pos.y < 0)
	{
		return 1;
	}
	return 0;
}

int projectileRencontreMur (terrain* T, projectile* A)
{
	assert (T != NULL);
	assert (A != NULL);
	if (!projectileSortDuTerrain (T, A))
	{
		int dimBloc = T->tab[0][0]->dim, i = (int)A->hitBox->pos.y/dimBloc, j = (int)A->hitBox->pos.x/dimBloc;
		if (contactBloc (A->hitBox, T->tab[i][j]) && T->tab[i][j]->solide > 1)
		{
			return 1;
		}
		return 0;
	}
	return 1;
}

int persoRencontreMurADroite (terrain* T, personnage* P)
{
	assert (T != NULL);
	assert (P != NULL);
	int dimBloc = T->tab[0][0]->dim, i = (int)P->hitBox->pos.y/dimBloc, j = (int)P->hitBox->pos.x/dimBloc;
	assert (i < T->hauteur);
	assert (j < T->largeur);
	if ((contactMur (P, T->tab[i][j+1]) && contactSol (P, T->tab[i+1][j])) || (contactMur (P, T->tab[i+1][j+1]) && !contactSol (P, T->tab[i+1][j])))
	{
		setPosition (P->hitBox, T->tab[i][j]->pos.x, P->hitBox->pos.y);
		return 1;
	}
	return 0;
}

int persoRencontreMurAGauche (terrain* T, personnage* P)
{
	assert (T != NULL);
	assert (P != NULL);
	int dimBloc = T->tab[0][0]->dim, i = (int)P->hitBox->pos.y/dimBloc, j = (int)P->hitBox->pos.x/dimBloc;
	assert (i < T->hauteur);
	assert (j < T->largeur);
	if ((contactMur (P, T->tab[i][j]) && contactSol (P, T->tab[i+1][j+1])) || (contactMur (P, T->tab[i+1][j]) && !contactSol (P, T->tab[i+1][j+1])))
	{
		setPosition (P->hitBox, T->tab[i][j+1]->pos.x, P->hitBox->pos.y);
		return 1;
	}
	return 0;
}

int persoSortDuSol (terrain* T, personnage* P)
{
	int dimBloc = T->tab[0][0]->dim, i = (int)P->hitBox->pos.y/dimBloc, j = (int)P->hitBox->pos.x/dimBloc;
	if ((contactSol(P, T->tab[i+1][j]) || contactSol(P, T->tab[i+1][j+1])) && (int)P->hitBox->pos.y%dimBloc == 0)
	{
		return 1;
	}
	return 0;
}

int persoAuSol (terrain* T, personnage* P)
{
	assert (T != NULL);
	assert (P != NULL);
	int dimBloc = T->tab[0][0]->dim, i = (int)P->hitBox->pos.y/dimBloc, j = (int)P->hitBox->pos.x/dimBloc;
	assert (i < T->hauteur);
	assert (j < T->largeur);
	if (contactSol (P, T->tab[i+1][j]) == 1 || contactSol (P, T->tab[i+1][j+1]) == 1)
	{
		return 1;
	}
	return 0;
}

void Gravite (terrain* Niveau, personnage* Perso, int vitesse)
{
	assert (Niveau != NULL);
	assert (Perso != NULL);
	assert (vitesse > 0);
	int dim = Niveau->tab[0][0]->dim;
	if (persoAuSol (Niveau, Perso) == 0)
	{
		blocMouvement (Perso->hitBox, 0, vitesse);
	}
	else
	{
		blocMouvement (Perso->hitBox, 0, -1*(int)(Perso->hitBox->pos.y)%dim);
	}
}

void sautPerso (personnage* Perso, const int hauteur)
{
	assert (Perso != NULL);
	assert (hauteur > 0);

	Perso->hitBox->pos.y -= hauteur;
}

int herosRencontreEnnemi (listeEnnemis* L, personnage* Heros)
{
	assert (L != NULL);
	assert (Heros != NULL);
	celluleEnnemi* Repere;
	int Place = 1;
	Repere = L->Boss;
	while (Repere != NULL)
	{
		if (!contactEnnemi (Heros, Repere->Ennemi))
		{
			Repere = Repere->Suivant;
			Place++;
		}
		else
		{
			return Place;
		}
	}
	return 0;
}

void degatsCorpsACorps (terrain* T, personnage* Heros, listeEnnemis* L, int position)
{
    int i;
	celluleEnnemi* Repere;
	assert (L != NULL);
	assert (Heros != NULL);
	assert (position > 0);
	assert (position <= L->nbEnnemis);
	Repere = L->Boss;
	for (i = 1 ; i<position ; i++)
	{
		Repere = Repere->Suivant;
	}
	personnageToucheEnnemi (Heros, Repere->Ennemi);
	if (Heros->sante < 0)
    {
        mortPersonnage(T, Heros);
    }
}

int projectileDansChargeur (personnage* P, projectile* Munition)
{
	assert (P != NULL);
	assert (Munition != NULL);
	if (Munition->dir == 0)
	{
		return 1;
	}
	return 0;
}

void Tir (projectile* P)
{
	switch (P->dir) /* Alors elle peut avancer selon les conditions ci-dessous : */
	{
		case (1) :
			blocMouvement (P->hitBox, 1*P->vitesse, 0); break; /* Mouvement a droite */
		case (2) :
			blocMouvement (P->hitBox, -1*P->vitesse, 0); break; /* Mouvement a gauche */
		case (3) :
			blocMouvement (P->hitBox, 0, -1*P->vitesse); break; /* Mouvement en haut */
		case (4) :
			blocMouvement (P->hitBox, 0, 1*P->vitesse); break; /* Mouvement en bas */
		case (5) :
			blocMouvement (P->hitBox, 1*P->vitesse, -1*P->vitesse); break; /* Mouvement en haut a droite */
		case (6) :
			blocMouvement (P->hitBox, -1*P->vitesse, -1*P->vitesse); break; /* Mouvement en haut a gauche */
		case (7) :
			blocMouvement (P->hitBox, 1*P->vitesse, 1*P->vitesse); break; /* Mouvement en bas a droite */
		case (8) :
			blocMouvement (P->hitBox, -1*P->vitesse, 1*P->vitesse); break; /* Mouvement en bas a gauche */
	}
}

void projectileHerosAvance (terrain* T, projectile* P, listeEnnemis* L, personnage* Heros)
{
	assert (T != NULL);
	assert (P != NULL);
	assert (L != NULL);
	assert (Heros != NULL);
	int posEnnemi = 0;
	if (projectileRencontreMur (T, P) == 0) /* Tant que la balle ne rencontre pas de mur : */
	{
		if (projectileRencontreEnnemi (L, P) == 0) /* Si elle ne rencontre aucun ennemi sur sa route : */
		{
			Tir (P);
		}
		else /* Sinon... */
		{
			posEnnemi = projectileRencontreEnnemi (L, P);
			infligeDegatsSurEnnemi (L, Heros, posEnnemi); /* ...elle lui inflige des degats... */
			setPosition(P->hitBox, 0, 0);
			P->dir = 0;
			/* ...et elle se recharge automatiquement */
		}
	}
	else
	{
		setPosition(P->hitBox, 0, 0);
		P->dir = 0;
	}
}

void progressionProjectilesHeros (terrain* T, listeEnnemis* L, personnage* Heros)
{
	int i;
	assert (T != NULL);
	assert (L != NULL);
	assert (Heros != NULL);
	for (i = 0 ; i < Heros->nbMunitions ; i++)
	{
		if (!projectileDansChargeur (Heros, Heros->munition[i]))
		{
			projectileHerosAvance (T, Heros->munition[i], L, Heros);
		}
	}
}

projectile* rechercheProchainProjectileUtilisable (personnage* P)
{
	int i;
	assert (P != NULL);
	for (i = 0 ; i < P->nbMunitions ; i++)
	{
		if (projectileDansChargeur (P, P->munition[i]))
		{
			setPosition (P->munition[i]->hitBox, P->hitBox->pos.x + P->hitBox->dim/2, P->hitBox->pos.y + P->hitBox->dim/2);
			return P->munition[i];
		}
	}
	return NULL;
}

void collisionTestRegression()
{
	int i;
	terrain Niveau;
	personnage Perso, Mechant, Boss;
	listeEnnemis ListeE;
	bloc B1, B2, hitBox, armePerso, hitBoss, hitBossProjectile;
	projectile Arme, ArmeMechant, armeBoss;

	initBloc(&B1);
	initBloc(&B2);
	initBloc(&hitBox);

	initTerrain (&Niveau);

	initPerso (&Perso);
	initPerso (&Mechant);
	initPerso (&Boss);

	initBloc (&hitBox);
	initBloc (&armePerso);
	initBloc (&hitBoss);
	initBloc (&hitBossProjectile);

	initProjectile (&Arme);
	initProjectile (&ArmeMechant);
	initProjectile (&armeBoss);

	setTerrain(&Niveau, "terrain.txt", 32);
	setBloc (&B1, 10, 1, 'T');

	setBloc (&hitBox, 32, 1, 'P');
	setBloc (&armePerso, 1, 1, '.');
	setBloc (&hitBoss, 300, 1, 'B');
	setBloc (&hitBossProjectile, 5, 1, '*');
	copieBloc (&B1, &B2);

	setProjectile (&Arme, 30, 10, &armePerso);
	setProjectile (&ArmeMechant, 20, 10, &armePerso);
	setProjectile (&armeBoss, 80, 15, &hitBossProjectile);

	setPersonnage (&Perso, 300, 3, 0, 160, 800, &Arme, 4, &hitBox);
	setPersonnage (&Mechant, 200, 1, 500, 300, 500, &ArmeMechant, 5, &hitBox);
	setPersonnage (&Boss, 300, 1, 2000, 3000, 100, &armeBoss, 3, &hitBoss);

	initListeEnnemis (&ListeE);

	ajouteEnnemi (&ListeE, &Boss, 6000, 1800, 3);
	ajouteEnnemi (&ListeE, &Mechant, 330, 270, 1);
	ajouteEnnemi (&ListeE, &Mechant, 640, 972, 5);

	setPosition (Mechant.hitBox, 170, 810);

	/* Verification de contactEnnemi */
	if (!contactEnnemi(&Perso, &Mechant))
	{
		printf ("Probleme avec contactEnnemi...\n\n");
	}
	else
	{
		printf("contactEnnemi est OK !\n\n");
	}

	setPosition (Perso.hitBox, 160, 800);
	setPosition (ArmeMechant.hitBox, 162, 800);

	/* Verification de contactProjectile */
	if (contactBloc(Perso.hitBox, ArmeMechant.hitBox) == 0)
	{
		printf ("Probleme avec contactProjectile...\n\n");
	}
	else
	{
		printf("contactProjectile est OK !\n\n");
	}

	setPosition (Arme.hitBox, 645, 980);

	/* Verification de projectileRencontreEnnemi */
	if (projectileRencontreEnnemi (&ListeE, &Arme) == 0 || projectileRencontreEnnemi (&ListeE, &Arme) != 2)
	{
		printf("Problème avec projectileRecontreEnnemi...\n\n");
	}
	else
	{
		printf("projectileRencontreEnnemi est OK ! \n\n");
	}

	setPosition (Arme.hitBox, 40, 100);

	/* Verification de projectileRencontreMur */
	if (projectileRencontreMur (&Niveau, &Arme) != 0)
	{
		printf("Probleme avec projectileRencontreMur...\n\n");
	}
	else
	{
		printf("projectileRencontreMur est OK !\n\n");
	}

	setPosition (Arme.hitBox, 325, 390);

	/* Verification de projectileRencontreMur... Encore... */
	if (!projectileRencontreMur (&Niveau, &Arme))
	{
		printf("Probleme avec projectileRencontreMur...\n\n");
	}
	else
	{
		printf("projectileRencontreMur est toujours OK !\n\n");
	}

	setPosition (Perso.hitBox, 310, 360);

	/* Verification de persoRencontreMurADroite */
	if (!persoRencontreMurADroite (&Niveau, &Perso))
	{
		printf("Probleme avec persoRencontreMurADroite...\n\n");
	}
	else if (Perso.hitBox->pos.x != 288)
	{
		printf("Probleme avec le repositionnement de persoRencontreMurADroite...\n\n");
	}
	else
	{
		printf("persoRencontreMurADroite est OK !\n\n");
	}

	setPosition (Perso.hitBox, 330, 360);

	/* Verification de persoRencontreMurAGauche */
	if (!persoRencontreMurAGauche (&Niveau, &Perso))
	{
		printf("Probleme avec persoRencontreMurAGauche...\n\n");
	}
	else if (Perso.hitBox->pos.x != 352)
	{
		printf("Probleme avec le repositionnement de persoRencontreMurAGauche...\n\n");
	}
	else
	{
		printf("persoRencontreMurAGauche est OK !\n\n");
	}

	printf("La position de Perso est y = %f\n\n", Perso.hitBox->pos.y);

	setPosition (Perso.hitBox, 1000, 390);

	/* Verification de persoAuSol */
	if (persoAuSol (&Niveau, &Perso) != 1)
	{
		printf("Probleme avec persoAuSol...\n\n");
	}
	else
	{
		printf("persoAuSol est OK !\n\n");
	}

	setPosition (Perso.hitBox, 1000, 200);
	i = 500;
	while (i != 0)
	{
		Gravite (&Niveau, &Perso, 10);
		i -= 10;
	}

	/*  Verification de Gravite */
	if (getPositionY (Perso.hitBox) == 200)
	{
		printf("Le Perso ne subit pas la Gravite...\n\n");
	}
	else if (getPositionY (Perso.hitBox) != 384 )
	{
		printf("Probleme de repositionnement avec Gravite...\n\n");
	}
	else
	{
		printf("Gravite est OK !\n\n");
	}

	setPosition (Perso.hitBox, 315, 280);

	/* Verification de herosRencontreEnnemi */
	if (!herosRencontreEnnemi (&ListeE, &Perso))
	{
		printf("Probleme avec herosRencontreEnnemi...\n\n");
	}
	else if (Perso.hitBox->pos.x != 283)
	{
		printf("Probleme avec la reposition de herosRencontreEnnemi...\n\n");
	}
	else
	{
		printf("herosRencontreEnnemi est OK !\n\n");
	}

	setPosition (Perso.hitBox, 420, 384);
	setPosition (ListeE.Boss->Suivant->Ennemi->hitBox, 480, 330);

	if (!projectileDansChargeur (&Perso, Perso.munition[0] ) )
	{
		printf("Probleme avec projectileDansChargeur...\n\n");
	}
	else
	{
		printf("projectileDansChargeur est OK !\n\n");
	}

	for (i = 0 ; i < 6 ; i++)
	{
		Gravite (&Niveau, ListeE.Boss->Suivant->Ennemi, 10);
		Gravite (&Niveau, &Perso, 10);
	}
	setDirectionTir (rechercheProchainProjectileUtilisable (&Perso), 1); /* Un evenement qui se declenche lorsque l'on enclenche la touche de tir */

	if (Perso.munition[0]->dir != 1)
	{
		printf("Probleme avec setDirectionTir ou rechercheProchainProjectileUtilisable...\n\n");
	}
	else
	{
		printf("setDirectionTir et rechercheProchainProjectileUtilisable sont OK !\n\n");
	}

	for (i = 0 ; i < 20 ; i++)
	{
		progressionProjectilesHeros (&Niveau, &ListeE, &Perso);
	}

	if (ListeE.Boss->Suivant->Ennemi->sante != 170)
	{
		printf("projectileHerosAvance n'inflige pas de degats a l'ennemi...\n\n");
	}
	else if (Perso.munition[0]->dir != 0)
	{
		printf("Probleme avec la reinitialisation de la direction d'un projectile...\n\n");
	}
	else if (getPositionX (Perso.munition[0]->hitBox) != 0 || getPositionY (Perso.munition[0]->hitBox) != 0)
	{
		printf("Probleme avec la reinitialisation de la position d'un projectile...\n\n");
	}
	else
	{
		printf("projectileHerosAvance et progressionProjectilesHeros sont OK !\n\n");
	}



	videTerrain (&Niveau);
	videListeEnnemis (&ListeE);
	videPerso (&Perso);
	videPerso (&Mechant);
	videPerso (&Boss);
	videProjectile (&Arme);
	videProjectile (&ArmeMechant);
	videProjectile (&armeBoss);
/*

*/

}
